<template>
  <div class="max-file">
    <el-upload drag action :auto-upload="false" :show-file-list="false" :on-change="changeFile">
      <i class="el-icon-upload"></i>
      <div class="el-upload__text">
        将文件拖到此处，或
        <em>点击上传</em>
      </div>
    </el-upload>

    <!-- PROGRESS -->
    <div class="progress">
      <span>上传进度：{{ total | totalText }}%</span>
      <el-link type="primary" v-if="total > 0 && total < 100" @click="handleBtn">{{
        btn | btnText
      }}</el-link>
    </div>

    <!-- VIDEO -->
    <div class="uploadImg" v-if="video">
      <video :src="video" controls />
    </div>
  </div>
</template>

<script>
import { fileParse } from "../utils/common";
import sparkMD5 from "spark-md5";
export default {
  name: "MaxFile",
  data() {
    return {
      total: 0,
      video: null,
      btn: false,
    };
  },
  filters: {
    btnText(btn) {
      return btn ? "继续" : "暂停";
    },
    totalText(total) {
      return total > 100 ? 100 : total;
    },
  },
  methods: {
    async changeFile(file) {
      if (!file) return;
      const fileData = file.raw
      const result = await fileParse(fileData, "buffer")
      const markerHash = new sparkMD5.ArrayBuffer()
      // 把buffer生成独立的hash值
      markerHash.append(result)
      // 生成成功
      const endHash = markerHash.end()
      // 获取文件后缀名
      const filename = /\.([0-9a-zA-Z]+)$/i.exec(fileData.name)[1]
      // 生成切片信息,创建100个切片
      const partList = [] // 存储切片信息
      const partSize = Math.ceil(fileData.size / 100) // 把文件长度分成100份，每份多少。这个可以根据自己喜欢
      let current = 0 // 初始值
      for (let i = 0; i < 100; i++) {
        let item = {
          chunk: fileData.slice(current, current + partSize),
          filename: `${endHash}_${i}.${filename}`
        };
        current += partSize
        partList.push(item)
      }
      // 挂载到当前实例，请求要用到
      this.partList = partList
      this.endHash = endHash
      this.sendRequest();
    },
    async sendRequest() {
      // 根据100个切片创造100个请求（集合）
      let requestList = []
      // 循环上传文件
      this.partList.forEach((item, index) => {
        // 每一个函数都是发送一个切片的请求
        let httpFn = () => {
          /**
           * FormData接口提供了一种方法，可以轻松地构造一组表示表单字段及其值的键/值对，
           * 然后可以使用XMLHttpRequest.send()方法轻松地发送这些值。
           * 如果将编码类型设置为“multipart/form-data”，它将使用与表单相同的格式
           */
          const formData = new FormData()
          formData.append('chunk', item.chunk)
          formData.append('filename', item.filename)
          return this.$http.post(
            '/api/single3',
            formData,
            {
              headers: {
                'Content-Type': 'multipart/form-data'
              }
            }
          ).then(res => {
            const result = res.data
            if (result.code == 200) {
              this.total += 1
              // 传完的切片我们把它移除掉，也可以记录上传后就不存在集合里面了
              this.partList.splice(index, 1)
            }
          })
        }
        requestList.push(httpFn)
      })
      this.uploadData(requestList)
    },
    // 统一上传
    uploadData (requestList) {
      /**
       * 传递：并行=并发(ajax.abort()强制中断上传)/串行(基于标识控制不发送)
       *  并发: 100个切片请求同时请求,浏览器的并发数有限制，一般每组只有7个，一组组传
       *  串行： 传完一个再传下一个
      */
     // 这是串行
     let i = 0
     let send = async () => {
       // 已经中断则不再上传
        if (this.abort) return
       if (i >= requestList.length) {
         this.getDirPath()
         return
       }
       await requestList[i]()
       i++
       send()
     }
     send()
    },
    // 获取上传返回的资源
    async getDirPath () {
      const result = await this.$http.get('/api/merge', {
        params: {
          hash: this.endHash
        }
      })
      const data = result.data
      if (data.code == 200) {
        this.video = data.path
      }
    },
    handleBtn() {
      if (this.btn) {
        //断点续传
        this.abort = false
        this.btn = false
        this.sendRequest()
        return
      }
      //暂停上传
      this.btn = true
      this.abort = true
    },
  },
};
</script>

<style lang="scss" scoped>
.max-file {
  box-sizing: border-box;
  padding: 20px;
  height: 100vh;
  display: flex;
  justify-content: flex-start;
  flex-direction: column;
  align-items: center;
}

.uploadImg {
  margin-top: 20px;
  width: 360px;

  img,
  video {
    display: block;
    width: 100%;
  }
}

.progress {
  box-sizing: border-box;
  margin-top: 20px;
  padding: 0 10px;
  width: 360px;
  height: 50px;

  display: flex;
  justify-content: space-between;
  align-items: center;
  background: lightcyan;
}
</style>
